/*
 * Copyright 2009 (c) Patrick Pietens.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */
package indigo.views
{
	import indigo.errors.ViewError;
	import indigo.events.ViewEvent;
	import indigo.utils.DisposableEventDispatcher;
	import indigo.utils.IDisposable;

	import flash.events.Event;
	import flash.utils.Dictionary;
	import flash.utils.getQualifiedClassName;

	/**
	 * Dispatches when a <code>View</code> is registered to the <code>ViewLocator</code> class.
	 * @eventType indigo.events.ViewEvent
	 */
	[Event(name="register", type="indigo.events.ViewEvent")]
	/**
	 * Dispatches when a <code>View</code> is unregistered from the <code>ViewLocator</code> class.
	 * @eventType indigo.events.ViewEvent
	 */
	[Event(name="unregister", type="indigo.events.ViewEvent")]
	/**
	 * The <code>ViewLocator</code> class is a Singleton which manages all 
	 * <code>Views</code> of your Indigo application.
	 * By registering them to the <code>ViewLocator</code> all 
	 * <code>Views</code> can be accessed without having to worry about its 
	 * position on the <code>DisplayList</code>. Use the <code>getView()</code> 
	 * shortcut to access <code>Views</code>. 
	 * 
	 * <p>It's best practice though to make <code>Views</code> react to changes 
	 * in the <code>ModelLocator</code> by listening to PropertyChangeEvents 
	 * instead of using the <code>getView()</code> shortcut. Use this </p>
	 * 
	 * <p><code>Views</code> will register and unregister themselves by default 
	 * when created and disposed.</p>
	 * 
	 * @example
	 * <listing version="3.0">
	 * </listing> 
	 * 
	 * @playerversion Flash 9
	 * @langversion ActionScript 3.0
	 * 
	 * @see getView
	 * @see hasView
	 * @see	View
	 */
	final public class ViewLocator extends DisposableEventDispatcher implements IDisposable
	{
		/**
		 * Reference to the single ViewLocator instance
		 */
		private static var INSTANCE:ViewLocator = null;
		/**
		 * Dictionary that holds references of all the Views used in the application 
		 */
		private var _viewList:Dictionary = new Dictionary( true );


		/**
		 * Constructor
		 * 
		 */
		public function ViewLocator( singleton:SingletonEnforcer )
		{
			log( "Instantiating" );
		}


		/**
		 * Registers a view to the ViewLocator. Once registered views can be 
		 * easily accessed by using the getView shortcut. Although it is 
		 * recommended to use the ModelLocator to communicate with Views.
		 * 
		 * @param View to register   
		 * @throws IndigoErrors.VIEW_ALREADY_REGISTERED when a View already exists
		 * 
		 */
		public function registerViewController( viewController:ViewController ):void
		{
			// Throws an error

			var myName:String = getQualifiedClassName( viewController );
			if ( this.hasView( viewController ) ) throw new ViewError( ViewError.VIEW_ALREADY_REGISTERED + ": " + myName );
			log( "Registering ViewController '" + myName + "' to ViewLocator" );
			// Register view
			this._viewList[ myName ] = viewController;
			// Dispatch event
			if ( this.hasEventListener( ViewEvent.REGISTER ) )
			{
				var myEvent:ViewEvent = new ViewEvent( ViewEvent.REGISTER );
				myEvent.viewController = viewController;
				this.dispatchEvent( myEvent );
			}
		}


		/**
		 * Unregisters a View	
		 * @param View to unregister
		 * @throws IndigoErrors.VIEW_NOT_FOUND when the View doesn't exists
		 * 
		 */
		public function unregisterViewController( viewController:ViewController ):void
		{
			// Throws an error
			var myName:String = getQualifiedClassName( viewController );
			if ( !this.hasView( viewController ) )
			{
				throw new ViewError( ViewError.VIEW_NOT_FOUND + ": " + myName );
			}
			log( "Unregistering ViewController '" + myName + "' to ViewLocator" );
			// Dispatch event
			if ( this.hasEventListener( ViewEvent.UNREGISTER ) )
			{
				var myEvent:ViewEvent = new ViewEvent( ViewEvent.UNREGISTER );
				myEvent.viewController = viewController;
				this.dispatchEvent( myEvent );
			}
			// Remove view
			this._viewList[ myName ] = null;
			delete this._viewList[ myName ];
		}


		/**
		 * Returns a reference to the View with a specific name. Use the 
		 * getView function as a shortcut to this method.
		 * 
		 * @param String that indicates the name of the View
		 * @return Reference of the View
		 * @throws ViewError when the View doesn't exists
		 * 
		 */
		public function getViewController( view:Class ):ViewController
		{
			var myName:String = getQualifiedClassName( view );
			return this._viewList[ myName ];
		}


		/**
		 * Returns a Boolean that indicates a View is registered to the 
		 * ViewLocator class.
		 * 
		 * @param String that indicates the name of the View
		 * @return Flag indicating the View exists
		 *  
		 */
		public function hasView( view:* ):Boolean
		{
			var myName:String = getQualifiedClassName( view );
			return this._viewList[ myName ] != null;
		}


		/**
		 * Unregisters all views from the ViewLocator and thus removing all 
		 * references to them
		 * 
		 */
		public function unregisterAllViews():void
		{
			for each ( var viewController:ViewController in this._viewList )
			{
				this.unregisterViewController( viewController );
			}
		}


		/**
		 * Returns a String representation of the ViewLocator
		 * @return String representation
		 * 
		 */
		override public function toString():String
		{
			var myString:String = "[ViewLocator ";
			for ( var name:String in this._viewList ) myString += name + "=" + this._viewList[ name ] + " ";
			myString += "]";
			return myString;
		}


		/**
		 * Returns a reference to the single ViewLocator instance
		 * @return	Instance of the ViewLocator
		 * 
		 */
		public static function getInstance():ViewLocator
		{
			if ( INSTANCE == null ) INSTANCE = new ViewLocator( new SingletonEnforcer );
			return INSTANCE;
		}


		/**
		 * @inheritDoc
		 * 
		 */
		override public function dispose( event:Event = null ):void
		{
			super.dispose( event );
			this.unregisterAllViews();
			this._viewList = null;
			INSTANCE = null;
		}
	}
}
internal class SingletonEnforcer
{
}